<?php
/**
 * Author: DefinitlyEvil
 * Created at: 2020/5/13 0:17
 */

namespace App\PublicAPI;


use App\Controller;
use App\Tools\Pattern;

class StorageController extends Controller
{

    public static function register(\FastRoute\RouteCollector $r){
        $r->post('/list', [self::class, 'do_list']);
        $r->post('/equip', [self::class, 'equip']);
        $r->post('/unequip', [self::class, 'unequip']);
    }

    public function do_list($params = []) {
        $filter = false;
        if(isset($params['f']) and is_array($params['f'])) {
            $filter = [];
            foreach($params['f'] as $k => $v) {
                // CHECK FOR PATTERN, PREVENT SQL INJECTION!
                if(!is_string($v) or !Pattern::matches(Pattern::ALPHANUMERIC, $v)) throw new \Exception('invalid category filter');
                $filter[] = sprintf("'%s'", $v); // double check
            }
            if(count($filter) <= 0) throw new \Exception('filter is empty');
            $filter = implode(',',$filter);
        }
        $sql = <<< EOL
SELECT
  `item_storage`.`id` AS `storage_id`,
  `item_types`.`id` AS `type_id`,
  `item_types`.`name` AS `type_name`,
  `item_types`.`tag` AS `type_tag`,
  `item_storage`.`equipped` AS `storage_equipped`,
  `item_categories`.`single_equip` AS `single_equip`,
  `item_categories`.`id` AS `category_id`,
  `item_categories`.`tag` AS `category_tag`,
  `item_categories`.`name` AS `category_name`,
  `item_storage`.`create_time` AS `storage_create_time`,
  IF(`item_storage`.`expire_time` IS NULL, TRUE, FALSE) AS `permanent`,
  `item_storage`.`expire_time` AS `storage_expire_time`,
  `item_types`.`display_image` AS `display_image`
FROM `item_storage`
  LEFT JOIN `item_types` ON `item_types`.`id`=`item_storage`.`item_type_id`
  LEFT JOIN `item_categories` ON `item_categories`.`id`=`item_types`.`category_id`
WHERE
  `account_id`=:account_id AND (`item_storage`.`expire_time` IS NULL OR UNIX_TIMESTAMP()<`item_storage`.`expire_time`)
EOL;
        if($filter !== false and is_string($filter)) {
            // already checked sql injection!
            $sql .= sprintf(" AND `item_categories`.`tag` IN (%s)", $filter);
        }
        $stm = $this->getPdo()->prepare($sql);
        $stm->bindParam(':account_id', $this->user['id']);
        if($stm->execute() === false) return ['database error / 1', false];
        $results = $stm->fetchAll(\PDO::FETCH_ASSOC);
        if($results === false) return ['database error / 2', false];
        return [['storage' => $results], true];
    }

    public function equip($params = []) {
        if (!isset($params['s']) or !is_int($params['s'])) return ['invalid params', false];
        $storageId = intval($params['s']);
        $pdo = $this->getPdo();

        // check single equip
        $stm = $pdo->prepare(<<<EOL
SELECT `item_categories`.`id` AS `category_id`,`item_categories`.`single_equip` AS `single_equip` FROM `item_storage`
LEFT JOIN `item_types`
  ON `item_storage`.`item_type_id` = `item_types`.`id`
LEFT JOIN `item_categories`
  ON `item_categories`.`id` = `item_types`.`category_id`
WHERE `item_storage`.`account_id`=:acc AND `item_storage`.`id`=:storage_id AND (`item_storage`.`expire_time` IS NULL OR UNIX_TIMESTAMP()<`item_storage`.`expire_time`)
EOL
        );
        $stm->bindParam(':acc', $this->user['id']);
        $stm->bindParam(':storage_id', $storageId);
        if ($stm->execute() === false) return ['check single equip failed', false];
        $single_fetch_result = $stm->fetch(\PDO::FETCH_ASSOC);
        if ($single_fetch_result === false) return ['item expired or not found', false];
        if (!$pdo->beginTransaction()) return ['start transaction failed', false];
        $ok = false;
        try {
            if (intval($single_fetch_result['single_equip']) > 0) {
                // do single equip
                $category_id = &$single_fetch_result['category_id'];
                $stm = $pdo->prepare(<<< EOL
UPDATE `item_storage`
LEFT JOIN `item_types` ON `item_storage`.`item_type_id`=`item_types`.`id`
LEFT JOIN `item_categories` ON `item_types`.`category_id`=`item_categories`.`id`
SET `item_storage`.`equipped`=0
WHERE `item_storage`.`account_id`=:acc AND `item_categories`.`id`=:cat
EOL
                );
                trigger_error('CAT: ' . $category_id);
                $stm->bindParam(':acc', $this->user['id']);
                $stm->bindParam(':cat', $category_id);
                if ($stm->execute() === false) return ['process single equip failed', false];
            }
            $stm = $pdo->prepare('UPDATE `item_storage` SET `equipped`=1 WHERE `id`=:id AND `account_id`=:acc');
            $stm->bindParam(':id', $storageId, \PDO::PARAM_INT);
            $stm->bindParam(':acc', $this->user['id'], \PDO::PARAM_INT);
            if ($stm->execute() === false or $stm->rowCount() <= 0) return ['update failed', false];
            if ($pdo->commit() === false) return ['save failed', false];
            $ok = true;
            return true;
        } finally {
            if ($ok !== true) {
                $pdo->rollBack();
            }
        }
    }

    public function unequip($params = []) {
        if(!isset($params['s']) or !is_int($params['s'])) return ['invalid params', false];
        $storageId = intval($params['s']);
        $pdo = $this->getPdo();
        $stm = $pdo->prepare('UPDATE `item_storage` SET `equipped`=0 WHERE `id`=:id AND `account_id`=:acc');
        $stm->bindParam(':id', $storageId);
        $stm->bindParam(':acc', $this->user['id'], \PDO::PARAM_INT);
        if($stm->execute() === false) return ['update failed', false];
        return true;
    }
}